<template>
	<view class="xt-input-main" :style="[wrapperStyle]" :class="[borderClass, { 'xt-input-round': round }]">
		<view class="xt-input-prepend">
			<slot name="prepend"></slot>
		</view>
		<view class="xt-input" @tap="xtClick">
			<input :style="[inputStyle]" :value="inputValue" :placeholder="placeholder" :type='type'
				:password="password || type === 'password' || false" :text-content-type='textContentType'
				:placeholder-class='placeholderClass' :placeholder-style="pStyle" :disabled="disabled || readonly"
				:maxlength="maxlength" :confirm-type="confirmType" :confirm-hold="confirmHold"
				:hold-keyboard="holdKeyboard" :cursor-spacing="cursorSpacing" :adjust-position="adjustPosition"
				:selection-end="selectionEnd" :selection-start="selectionStart" :random-number="randomNumber"
				:controlled="controlled" :always-system="alwaysSystem" :inputmode="inputmode" :cursor="cursor"
				:cursor-color="cursorColor" :auto-blur='autoBlur' :ignoreCompositionEvent='ignoreCompositionEvent'
				:always-embed='alwaysEmbed' :safe-password-cert-path="safePasswordCertPath"
				:safe-password-custom-hash="safePasswordCustomHash" :safe-password-length="safePasswordLength"
				:safe-password-time-stamp="safePasswordTimeStamp" :safe-password-nonce="safePasswordNonce"
				:safe-password-salt="safePasswordSalt" @input="xtInput" @focus='xtFocus' @blur='xtBlur'
				@confirm='xtConfirm' @keyboardheightchange='xtKeyboardheightchange'></input>
			<view class="xt-input-del" v-if="isDel" @tap="inputDel">
				<view></view>
				<view></view>
			</view>
		</view>
		<view class="xt-input-append">
			<slot name="append"></slot>
		</view>
	</view>
</template>
<script>
	// #ifdef VUE3
	import {
		nextTick
	} from 'vue'
	// #endif
	import props from "./props.js";
	/**
	 * 
	 ** Input 输入框
	 * @description 本组件提供一个输入框的功能，让开发者开发起来更加得心应手。减少重复的模板代码
	 * @tutorial https://ext.dcloud.net.cn/plugin?name=xt-input
	 * 
	 * @property {String | Number} 		value 				 		输入框的值。
	 * @property {String}				fontAlign					输入框内容对齐方式(默认 'left')
	 * @property {String}				fontSize					输入框字体的大小(默认 '30rpx')
	 * @property {String}				color						输入框字体颜色(默认 '#303133')
	 * @property {Object}				customStyle					自定义样式
	 * @property {String} 				type  				 		输入框的类型。默认为 `'text'`。可以设置为 `text`、`password` 等。
	 * @property {String} 				textContentType  	 		输入框的文本内容类型，仅在 AppnvueiOS 中生效，用于设置特定的输入内容类型。
	 * @property {String} 				border  					输入框的边框样式。默认为 surround。可选 bottom,none
	 * @property {Boolean} 				round  			 			是否为圆角类型的输入框。默认为 `false`。
	 * @property {Boolean} 				password  			 		是否为密码类型的输入框。默认为 `false`。
	 * @property {Boolean} 				stop  			 			是否阻止事件冒泡。默认为 `false`。
	 * @property {String} 				placeholder  		 		输入框为空时的占位符文本。
	 * @property {String | Object} 		placeholderStyle  	 		占位符样式，可以是字符串或对象。默认为 `''`。
	 * @property {String} 				placeholderClass   	 		占位符样式类。默认为 `'inputplaceholder'`，如果页面的样式是 scoped，需要使用 `/deep/` 来应用样式。
	 * @property {Boolean} 				readonly  					只读。默认为 `false`。
	 * @property {Boolean} 				disabled  					是否禁用输入框。默认为 `false`。
	 * @property {String} 				disabledColor  				禁用时输入框的背景颜色。默认为 `false`。
	 * @property {Boolean} 				clearable  			 		是否可清空。默认为 `false`。
	 * @property {Number} 				maxlength  			 		输入框的最大输入长度。默认为 `140`。
	 * @property {Number} 				cursorSpacing  		 		输入框与键盘之间的最小距离，单位为像素。默认为 `0`。
	 * @property {Boolean} 				focus  				 		是否获取焦点。默认为 `false`。
	 * @property {String} 				confirmType  		 		键盘右下角按钮的文字，仅在 `type="text"` 时生效。默认为 `'done'`。
	 * @property {Boolean} 				confirmHold  				点击键盘右下角按钮时是否保持键盘不收起。默认为 `false`。
	 * @property {Number} 				cursor  			 		指定焦点时光标的位置。
	 * @property {String} 				cursorColor  		 		设置光标的颜色。
	 * @property {Number} 				selectionStart  	 		设置自动聚焦时光标的起始位置。与 `selectionEnd` 配合使用。
	 * @property {Number} 				selectionEnd  		 		设置自动聚焦时光标的结束位置。与 `selectionStart` 配合使用。
	 * @property {Boolean} 				adjustPosition  	 		键盘弹起时是否自动上推页面。默认为 `true`。
	 * @property {Boolean} 				autoBlur  			 		键盘收起时是否自动失去焦点。默认为 `false`。
	 * @property {Boolean} 				ignoreCompositionEvent  	是否忽略文本合成系统事件。默认为 `true`
	 * @property {Boolean} 				alwaysEmbed  				是否强制输入框保持在同一层级，仅在 iOS 下生效。默认为 `false`。
	 * @property {Boolean}				holdKeyboard  				是否在 focus 时，点击页面时不收起键盘。默认为 `false`。
	 * @property {String} 				safePasswordCertPath  		安全键盘加密公钥的路径，只支持包内路径。
	 * @property {Number} 				safePasswordLength  		安全键盘的密码长度。
	 * @property {Number} 				safePasswordTimeStamp  		安全键盘的加密时间戳。
	 * @property {String} 				safePasswordNonce  			安全键盘的加密盐值。
	 * @property {String} 				safePasswordSalt  			安全键盘计算 hash 时使用的盐值。如果指定 `customhash`，则此项无效。
	 * @property {String} 				safePasswordCustomHash  	安全键盘计算 hash 时的算法表达式，例如 `md5(sha1('foo' + sha256(sm3(password + 'bar'))))`。
	 * @property {Boolean} 				randomNumber  				是否在数字键盘（`number`、`digit`、`idcard` 类型）上随机排列数字。默认为 `false`。
	 * @property {Boolean} 				controlled  				是否为受控组件。为 `true` 时，`value` 内容会完全由 `setData` 控制。默认为 `false`。
	 * @property {Boolean} 				alwaysSystem  				是否强制使用系统键盘和 Webview 创建的输入元素。为 `true` 时，`confirmtype` 和 `confirmhold` 可能会失效。默认为 `false`。
	 * @property {String} 				inputmode  					输入框的输入模式。可以是 `text`、`numeric` 等，默认为 `'text'`。
	 * @property {Function}				formatter			    	内容式化函数。
	 * @event 	{Function} 				click  						输入框点击事件。
	 * @event 	{Function} 				input  						输入框输入事件。
	 * @event 	{Function} 				focus  						输入框聚焦事件。
	 * @event 	{Function} 				blur  						输入框失去焦点事件。
	 * @event 	{Function} 				confirm  					点击完成按钮时触发。
	 * @event 	{Function} 				keyboardheightchange  		键盘高度发生变化的事件。
	 * @event 	{Function} 				clear  						输入框清空时触发。
	 * @example <xt-input></xt-input>
	 */
	export default {
		name: "xt-input",
		mixins: [props],
		data() {
			return {
				inputValue: "",
				borderClass: '',
				pStyle: '',
				// 焦点状态
				focused: false,
				// 是否格式化
				isFormat: false,
				// 内部变化
				internal: false,
				// 是否第一次变化
				firstChange: true,

			};
		},
		computed: {
			isDel() {
				return this.inputValue && this.clearable && this.focused && !this.readonly && !this.disabled;
			},
			inputStyle() {
				let style = {
					color: this.color,
					fontSize: this.fontSize,
					textAlign: this.fontAlign
				};
				return style;
			},
			wrapperStyle() {
				const style = {};
				if (this.disabled) {
					style.backgroundColor = this.disabledColor;
				}
				return {
					...style,
					...this.customStyle
				};
			},
		},
		watch: {
			value: {
				immediate: true,
				handler(newVal, oldVal) {
					if (this.internal || this.inputValue === newVal) {
						this.internal = false;
						return;
					}
					this.inputValue = newVal;
					if (this.firstChange) {
						this.formValidate(this, "change")
					}
					// 在WEB中，外部value变化后，修改input中的值，不会触发@input事件，此时手动调用值变化方法
					if (
						this.firstChange === false &&
						this.internal === false
					) {
						this.inputChange(this.inputValue, false);
					} else {
						// 调用xt-form的校验
						if (!this.firstChange) this.formValidate(this, "change");
					}
					this.firstChange = false;
					this.internal = false;
				}
			},
			// #ifdef VUE3
			modelValue: {
				immediate: true,
				handler(newVal, oldVal) {
					if (this.internal || this.inputValue === newVal) {
						this.internal = false;
						return;
					}
					if (this.value != '' && this.value != void 0 && this.value != null) {
						this.inputValue = this.value;
						return
					}
					this.inputValue = newVal;
					if (this.firstChange) {
						this.formValidate(this, "change")
					}
					// 在WEB中，外部value变化后，修改input中的值，不会触发@input事件，此时手动调用值变化方法
					if (
						this.firstChange === false &&
						this.internal === false
					) {
						this.inputChange(this.inputValue, false);
					} else {
						// 调用xt-form的校验
						if (!this.firstChange) this.formValidate(this, "change");
					}
					this.firstChange = false;
					this.internal = false;
				}
			},
			// #endif
		},
		methods: {
			getParent(name = undefined) {
				let parent = this.$parent
				while (parent) {
					// 父组件
					if (parent.$options && parent.$options.name !== name) {
						// 如果组件的name不相等，继续上一级寻找
						parent = parent.$parent
					} else {
						return parent
					}
				}
				return false
			},
			formValidate(instance, event) {
				const formItem = this.getParent.call(instance, 'xt-form-item')
				const form = this.getParent.call(instance, 'xt-form')
				if (formItem && form) {
					form.validateField(formItem.prop, () => {}, event)
				}
			},
			handleVue(func) {
				// #ifdef VUE2
				this.$nextTick(() => {
					func();
				})
				// #endif
				// #ifdef VUE3
				nextTick(() => {
					func();
				})
				// #endif
			},
			xtClick(event) {
				this.$emit("click");
				this.stop && event && typeof event.stopPropagation === 'function' && event.stopPropagation();
			},
			xtInput(e) {
				this.inputValue = e.detail.value;
				const value = this.isFormat ? this.formatter(e.detail.value) : e.detail.value;
				this.handleVue(() => {
					this.inputChange(value);
				})
			},
			xtBlur(e) {
				this.$emit("blur", e);
				setTimeout(() => {
					this.focused = false;
				}, 300)
				this.formValidate(this, "blur");
			},
			xtFocus(e) {
				this.focused = true;
				this.$emit("focus", e);
			},
			xtConfirm(e) {
				this.$emit("confirm", e);
			},
			xtKeyboardheightchange(e) {
				this.$emit("keyboardheightchange", e);
			},
			inputChange(e, xt = true) {
				const value = e || this.inputValue;
				this.inputValue = value;
				this.handleVue(() => {
					// #ifdef VUE3
					this.$emit("update:modelValue", value);
					// #endif
					this.internal = xt;
					this.$emit("input", value);
				})
				this.formValidate(this, "change");
			},
			inputDel() {
				this.inputValue = "";
				this.handleVue(() => {
					this.inputChange();
					this.$emit("clear");
				})
			},
			handleStyle(obj) {
				return Object.entries(obj)
					.map(([key, value]) => `${key}:${value}`)
					.join(';');
			}
		},
		mounted() {
			this.borderClass = 'xt-input-border-' + this.border;
			this.pStyle = typeof this.placeholderStyle === 'string' ? this.placeholderStyle : this.handleStyle(this
				.placeholderStyle);
			if (typeof this.formatter === 'function') {
				this.isFormat = true;
			} else {
				this.isFormat = false;
			}
		}
	};
</script>
<style scoped lang="scss">
	@import "./style.scss";
</style>